#include "unity.h"
#include "auth_service.h"
#include "user_repository.h"
#include "session_repository.h"
#include "database_manager.h"
#include <iostream>
#include <thread>
#include <chrono>

// Global pointers for testing - will be initialized in setUp
static DatabaseManager* dbManager = nullptr;
static UserRepository* userRepository = nullptr;
static SessionRepository* sessionRepository = nullptr;
static AuthService* authService = nullptr;

// Test result flags
static bool login_success_event = false;
static bool login_failure_event = false;
static bool register_success_event = false;
static bool register_failure_event = false;
static bool logout_success_event = false;
static bool logout_failure_event = false;
static bool session_authenticated_event = false;
static bool session_unauthenticated_event = false;

// Event handlers
static void onLoginSuccess() {
    login_success_event = true;
    std::cout << "Login success event received" << std::endl;
}

static void onLoginFailure() {
    login_failure_event = true;
    std::cout << "Login failure event received" << std::endl;
}

static void onRegisterSuccess() {
    register_success_event = true;
    std::cout << "Register success event received" << std::endl;
}

static void onRegisterFailure() {
    register_failure_event = true;
    std::cout << "Register failure event received" << std::endl;
}

static void onLogoutSuccess() {
    logout_success_event = true;
    std::cout << "Logout success event received" << std::endl;
}

static void onLogoutFailure() {
    logout_failure_event = true;
    std::cout << "Logout failure event received" << std::endl;
}

static void onSessionAuthenticated() {
    session_authenticated_event = true;
    std::cout << "Session authenticated event received" << std::endl;
}

static void onSessionUnauthenticated() {
    session_unauthenticated_event = true;
    std::cout << "Session unauthenticated event received" << std::endl;
}

void setUp(void)
{
    // This is run before EACH test
    // Create a unique database file for this test
    static int test_counter = 0;
    std::string db_path = "data/test_event_driven_auth_" + std::to_string(++test_counter) + ".db";
    
    // Create data directory if it doesn't exist
    system("mkdir -p data");
    
    // Initialize database manager with unique database file
    dbManager = new DatabaseManager(db_path);
    userRepository = new UserRepository(dbManager->getDatabaseConnection());
    sessionRepository = new SessionRepository(dbManager->getDatabaseConnection());
    authService = new AuthService(*userRepository, *sessionRepository);
    
    // Initialize database
    dbManager->initialize();
    
    // Reset test result flags
    login_success_event = false;
    login_failure_event = false;
    register_success_event = false;
    register_failure_event = false;
    logout_success_event = false;
    logout_failure_event = false;
    session_authenticated_event = false;
    session_unauthenticated_event = false;
    
    // Register event listeners
    authService->getEventDispatcher().appendListener("AUTH_LOGIN_RESPONSE_SUCCESS", onLoginSuccess);
    authService->getEventDispatcher().appendListener("AUTH_LOGIN_RESPONSE_FAILURE", onLoginFailure);
    authService->getEventDispatcher().appendListener("AUTH_REGISTER_RESPONSE_SUCCESS", onRegisterSuccess);
    authService->getEventDispatcher().appendListener("AUTH_REGISTER_RESPONSE_FAILURE", onRegisterFailure);
    authService->getEventDispatcher().appendListener("AUTH_LOGOUT_RESPONSE_SUCCESS", onLogoutSuccess);
    authService->getEventDispatcher().appendListener("AUTH_LOGOUT_RESPONSE_FAILURE", onLogoutFailure);
    authService->getEventDispatcher().appendListener("AUTH_SESSION_STATUS_AUTHENTICATED", onSessionAuthenticated);
    authService->getEventDispatcher().appendListener("AUTH_SESSION_STATUS_UNAUTHENTICATED", onSessionUnauthenticated);
    
    std::cout << "Test environment initialized" << std::endl;
}

void tearDown(void)
{
    // This is run after EACH test
    // Clean up allocated memory
    delete authService;
    delete sessionRepository;
    delete userRepository;
    delete dbManager;
    
    // Remove the test database file
    system("rm -f data/test_event_driven_auth_*.db");
}

// Test event-driven async login functionality
void test_event_driven_async_login(void)
{
    // Register a user first
    authService->registerUser("testuser", "testpass");
    
    // Wait a bit for registration to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Now test login
    authService->login("testuser", "testpass");
    
    // Wait for login to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Verify events were dispatched
    TEST_ASSERT_TRUE(register_success_event);
    TEST_ASSERT_TRUE(login_success_event);
    TEST_ASSERT_FALSE(login_failure_event);
}

// Test event-driven async login with wrong password
void test_event_driven_async_login_wrong_password(void)
{
    // Register a user first
    authService->registerUser("testuser", "testpass");
    
    // Wait a bit for registration to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Now test login with wrong password
    authService->login("testuser", "wrongpass");
    
    // Wait for login to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Verify events were dispatched
    TEST_ASSERT_TRUE(register_success_event);
    TEST_ASSERT_FALSE(login_success_event);
    TEST_ASSERT_TRUE(login_failure_event);
}

// Test event-driven async logout functionality
void test_event_driven_async_logout(void)
{
    // Register a user first
    authService->registerUser("testuser", "testpass");
    
    // Wait a bit for registration to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Login
    authService->login("testuser", "testpass");
    
    // Wait for login to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Test logout (we'll use user ID 1 as that should be the first user)
    authService->logout(1);
    
    // Wait for logout to complete
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Verify events were dispatched
    TEST_ASSERT_TRUE(register_success_event);
    TEST_ASSERT_TRUE(login_success_event);
    TEST_ASSERT_TRUE(logout_success_event);
    TEST_ASSERT_FALSE(logout_failure_event);
}

int main(void)
{
    UNITY_BEGIN();
    RUN_TEST(test_event_driven_async_login);
    RUN_TEST(test_event_driven_async_login_wrong_password);
    RUN_TEST(test_event_driven_async_logout);
    return UNITY_END();
}